package com.rambler.fileupload.facade;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rambler.common.response.CommonResponse;
import com.rambler.common.utils.FileUtil;
import com.rambler.fileupload.converters.FileInfoTransfer;
import com.rambler.api.fileupload.dto.FileMetadata;
import com.rambler.api.fileupload.dto.FileUploadDTO;
import com.rambler.fileupload.entity.SysFileInfo;
import com.rambler.fileupload.mapper.FileUploadMapper;
import com.rambler.fileupload.processor.ImageProcessor;
import com.rambler.fileupload.service.FileUploadService;
import com.rambler.fileupload.service.UploadServiceFactory;
import com.rambler.api.fileupload.vo.FileUploadVO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;
import java.util.Objects;

/**
 * @author suhao
 * 文件上传的门面
 * 通过工厂模式注入其他业务实现类
 */
@Service
public class FileUploadFacadeService extends ServiceImpl<FileUploadMapper, SysFileInfo> {

    @Resource
    private UploadServiceFactory uploadServiceFactory;

    @Resource
    private ImageProcessor imageProcessor;

    @Resource
    protected FileUploadMapper fileInfoMapper;

    @Resource
    private FileInfoTransfer fileInfoTransfer;

    public CommonResponse<FileUploadVO> uploadFile(MultipartFile file, String path, FileUploadDTO dto) {

        // 文件元信息
        fillupFileMetaInfo(file, dto);

        // 处理图片, 在最前面处理
        // 因为同一张图片不同处理方式上传时, 在处理前源文件的md5和sha1都一样
        if (null != dto.getImageProcessOption()) {
            // 处理完图片重新赋值给file
            file = imageProcessor.processImage(file, dto);
        }

        // 计算md5和sha1, 填充其他元信息
        calcMd5AndSha1(file, dto);

        // 判断文件是否存在, 这里先
        SysFileInfo existFileInfo = isFileExist(dto);
        if (existFileInfo != null) {
            return CommonResponse.failure("文件已存在");
        }

        // 解析excel
        if (null != dto.getExcelProcessOption()) {
            // TODO excel解析
            System.out.println("excelProcessOption");
        }

        // 上传
        FileUploadService service = uploadServiceFactory.getService(dto.getUploadType());
        try {
            SysFileInfo sysFileInfo = service.uploadFile(file, path, dto);
            // 文件元信息填充到实体中, 保存到数据库
            fillupFileInfoFromMetadata(sysFileInfo, dto.getFileMetadata());
            fileInfoMapper.insert(sysFileInfo);
            // 保存到数据库
            return CommonResponse.success("上传成功", fileInfoTransfer.toVO(sysFileInfo));
        } catch (Exception e) {
            return CommonResponse.failure("上传失败");
        }
    }

    // 从元信息填充fileInfo
    private void fillupFileInfoFromMetadata(SysFileInfo fileInfo, FileMetadata fileMetadata) {
        fileInfo.setFileSize(fileMetadata.getSize());
        fileInfo.setMd5(fileMetadata.getMd5());
        fileInfo.setSha1(fileMetadata.getSha1());
        fileInfo.setContentType(fileMetadata.getContentType());
        fileInfo.setOriginalFileName(fileMetadata.getOriginalFileName());
        fileInfo.setFileSuffix(fileMetadata.getExtension());
    }

    /**
     * 填充文件元信息
     *
     * @param file 文件
     * @param dto  dto
     */
    private void fillupFileMetaInfo(MultipartFile file, FileUploadDTO dto) {
        FileMetadata fileMetadata = new FileMetadata();
        // 其他元信息
        fileMetadata.setOriginalFileName(file.getOriginalFilename());
        fileMetadata.setContentType(file.getContentType());
        // 文件名和后缀
        fileMetadata.setExtension(FileUtil.getExtension(Objects.requireNonNull(file.getOriginalFilename())));
        fileMetadata.setPrefix(file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf(".")));
        dto.setFileMetadata(fileMetadata);
        dto.setFileMetadata(fileMetadata);
    }

    /**
     * 计算md5和sha1
     *
     * @param file file
     * @param dto  dto
     */
    private void calcMd5AndSha1(MultipartFile file, FileUploadDTO dto) {
        try {
            FileMetadata fileMetadata = dto.getFileMetadata();
            String md5 = FileUtil.calculateMd5(file.getInputStream());
            String sha1 = FileUtil.calculateSha1(file.getInputStream());
            // 计算md5, sha1
            fileMetadata.setMd5(md5);
            fileMetadata.setSha1(sha1);
            // 图片可能被压缩过, size就需要重新取
            fileMetadata.setSize(file.getSize());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 检查md5和sha1看是否重复
     *
     * @param dto 文件
     * @return 重复的文件信息或者 null
     */
    private SysFileInfo isFileExist(FileUploadDTO dto) {
        LambdaQueryWrapper<SysFileInfo> query = new LambdaQueryWrapper<>();
        // md5和sha1双重校验，避免哈希碰撞导致误判
        FileMetadata fileMetadata = dto.getFileMetadata();
        query.eq(SysFileInfo::getMd5, fileMetadata.getMd5());
        query.eq(SysFileInfo::getSha1, fileMetadata.getSha1());
        // 已存在，直接返回已有文件信息
        return fileInfoMapper.selectOne(query);
    }


    /**
     * 根据id查询文件信息
     *
     * @param id 文件id
     * @return 文件信息
     */
    public FileUploadVO selectById(String id) {
        SysFileInfo sysFileInfo = fileInfoMapper.selectById(id);
        return fileInfoTransfer.toVO(sysFileInfo);
    }

    /**
     * 根据条件查询文件列表
     * @param queryWrapper
     * @return
     */
    public List<FileUploadVO> selectList(LambdaQueryWrapper<SysFileInfo> queryWrapper) {
        List<SysFileInfo> sysFileInfos = fileInfoMapper.selectList(queryWrapper);
        return sysFileInfos.stream().map(fileInfoTransfer::toVO).toList();
    }
}
